/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "Thread.h"
#include "Tracer.h"
#include <time.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <signal.h>

#define PTHREAD_ATTR_ENABLE     0   /* 是否使能线程属性控制 */
namespace libemb{

int ThreadAttr::policy()
{
	return m_policy;
}
int ThreadAttr::priority()
{
	return m_priority;
}
int ThreadAttr::inherit()
{
	return m_inherit;
}
int ThreadAttr::stackSize()
{
	return m_stackSize;
}

/**
 *  \class  ThreadArgs
 *  \brief  任务参数类
 */
class ThreadArgs{
public:
    Threading* m_owner;
    ThreadEntry m_entry;
    void* m_args;
};

/**
 *  \brief  线程化函数
 *  \param  task 任务方法
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int Threading::threading(ThreadEntry entry,void* args)
{
    pthread_t threadID;
    /* 一定要使用malloc分配arg内存,等线程跑起来后再free;
     * 千万不能直接定义一个临时变量,否则会出现段错误;
     * 原因是:pthread_create返回时线程实际还没运行,
     * 而返回后此函数退出,定义的变量已经失效,因此出错!
     */
    auto threadArgs = std::make_unique<ThreadArgs>();
    threadArgs->m_owner=this;
    threadArgs->m_entry=entry;
    threadArgs->m_args=args;
	if(entry == NULL)
	{
	    TRACE_ERR_CLASS("parameter error: thread entry=NULL!\n");
		return RC_ERROR;
	}
	if(0!=pthread_create(&threadID, NULL, startRoutine, threadArgs.release()))
	{
        TRACE_ERR_CLASS("threading failed:%s!\n", ERRSTR); 
		return RC_ERROR;
	}
	return RC_OK;   
}

/* 任务入口点 */
void * Threading::startRoutine(void *arg)
{
    pthread_detach(pthread_self());
	std::unique_ptr<ThreadArgs> threadArgs((ThreadArgs*)arg);
	if(threadArgs->m_owner!=NULL && threadArgs->m_entry!=NULL)
	{
		threadArgs->m_entry(threadArgs->m_args);
	}
	return (void*)0;
}

void Runnable::quit()
{
	m_runFlag = false;
}

bool Runnable::isRunning()
{
    return m_runFlag;
}

/**
 *  \brief  增加线程取消点
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   当线程取消时,程序运行到此函数线程才退出
 */
bool Runnable::checkCancel()
{
    #ifdef OS_ANDROID
    //TRACE_ERR_CLASS("Not support thread cancel!\n");
    return false;
    #else
    pthread_testcancel();
    return true;
    #endif
}

Thread::Thread()
{
}

Thread::~Thread()
{
	forceQuit();
}

/**
 *  \brief  初始化实时抢占系统
 *  \param  void
 *  \return 成功返回true,失败返回false
 *  \note   此函数仅限在linux-rt上使用
 */
bool Thread::initWithPreemptRT(int policy, int priority)
{
	/* 锁定当前进程内存,防止被系统SWAP到硬盘中去,影响实时性能 */
	if(mlockall(MCL_CURRENT|MCL_FUTURE) != 0) 
	{
		return false;
	}

	#if 0
    {
    	/* 栈空间预留:Linux进程栈空间最大为8M
    	 * 故意让栈空间往下生长8K,其后的函数调用和局部变量将不再导致
    	 * 栈增长(依赖于page fault和内存申请)
    	 */
    	unsigned char dummy[8096];
    	memset(dummy, 0, 8096);
	}
	#endif
	/* 设置当前进程调度策略和优先级 */
	pid_t pid = getpid();
	struct sched_param param;
	param.sched_priority = priority;
	if(sched_setscheduler(pid, policy, &param)<0)
	{
		TRACE_ERR("main process(%d) cannot set policy:%d, priority:%d.\n",pid,policy,priority);
		return false;
	}
	return true;
}


/**
 *  \brief  微秒延时函数
 *  \param  us 要延时的微秒数
 *  \return void
 *  \note   函数休眠时,当前进程可能会让出CPU,引发进程调度
 *          注意使用usleep时,时间不能设置得太短,否则调度时
 *          进程间切换太频繁非常耗资源!!!推荐最小值为100us
 */
void Thread::usleep(int us)
{
	/* 注意:sleep和select等带有睡眠阻塞性质的函数一般都可能会被信号打断,此时会立即返回,达不到睡眠的效果 */
	#if 0
	/* 一般情况下不需要十分精确的定时,所以这里利用select超时机制来定时 */
	struct timeval tv;
	if (us<=0) 
	{
		us = 1;
	}

	tv.tv_sec = us/1000000;
	if (tv.tv_sec==0)
	{
		tv.tv_usec = us;
	}
	else
    {
    	tv.tv_usec = us%1000000;
	}
	select(0,NULL,NULL,NULL,&tv);
	#else
	struct timespec ts;
	if (us<=0) 
	{
		us = 1;
	}
    ts.tv_sec = us/1000000;
	if (ts.tv_sec==0)
	{
		ts.tv_nsec = us*1000;
	}
    else
    {
    	ts.tv_nsec = (us%1000000)*1000;
    }
	clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);/* 使用MONITONIC时钟,不受更改系统时钟的影响 */
	#endif
}

/**
 *  \brief  毫秒延时函数
 *  \param  ms 要延时的毫秒数
 *  \return void
 *  \note   函数休眠时,当前进程可能会让出CPU,引发进程调度
 */
void Thread::msleep(int ms)
{
	Thread::usleep(ms*1000);
}


void Thread::setAttribute(int policy, int priority, bool inherit,int stackSize)
{
	m_threadAttribute.m_policy = policy;
	if (policy==ThreadAttr::SCHED_POLICY_OTHER)
	{
		priority = 0;
	}
	m_threadAttribute.m_priority = priority;
	m_threadAttribute.m_stackSize = stackSize;
	m_threadAttribute.m_inherit = inherit?PTHREAD_INHERIT_SCHED:PTHREAD_EXPLICIT_SCHED;
	if (stackSize<PTHREAD_STACK_MIN)
	{
		m_threadAttribute.m_stackSize = PTHREAD_STACK_MIN;
	}
	else
	{
		m_threadAttribute.m_stackSize = stackSize;
	}
}

/**
 *  \brief  启动线程
 *  \param  runnable Runnable对象
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool Thread::start(const Runnable& runnable)
{
	pthread_attr_t* pAttr = NULL;
	if(m_threadID != 0)
	{
		TRACE_ERR_CLASS("Param Error,threadID:%d\n",m_threadID);
		return false;
	}
    
	m_pRunnable = const_cast<Runnable*>(&runnable);
	m_pRunnable->m_runFlag = true;
	/* 设置线程属性 */
	pthread_attr_init(&m_attribute);
	if(pthreadAttrSet(m_attribute))
	{
		pAttr = &m_attribute;
	}
	if(0!=pthread_create(&m_threadID, pAttr, startRoutine, this))
	{
        TRACE_ERR_CLASS("%s create thread failed:%s!\n", CLASS_NAME(m_pRunnable),ERRSTR); 
		m_pRunnable->m_runFlag = false;
		return false;
	}
	m_threadStatus = STATE_START;
    //TRACE_REL_CLASS("%s create thread(%u) ok.\n",CLASS_NAME(m_pRunnable),m_threadID); 
    pthread_attr_destroy(&m_attribute);
	return true;
}
/**
 *  \brief  停止线程
 *  \param  usTimeout 超时时间
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool Thread::stop(int usTimeout)
{
	m_pRunnable->quit();
	usTimeout = MAX(100,usTimeout);
    while (usTimeout>0) 
    {
    	if(m_threadStatus==STATE_EXIT)
    	{
    		m_threadID = 0; 
			return true;
		}
       	usleep(100);
		usTimeout -= 100;
    }
	return false;
}

/**
 *  \brief  强制退出线程
 *  \param  void
 *  \return void
 *  \note   
 */
bool Thread::forceQuit()
{
#ifdef OS_ANDROID
	return false;
#else
    if (m_threadID > 0)
    {
        /* 执行pthread_cancel后,并不会直接取消线程,必须等到下一次系统调用或者pthread_testcancel才会真正取消线程 */
    	if (0!=pthread_cancel(m_threadID))
    	{
            TRACE_ERR_CLASS("pthread cancel error:%s.\n", ERRSTR); 
    		return false;
    	}
        pthread_join(m_threadID, NULL);
    }
	m_threadStatus = STATE_EXIT;
    m_threadID = 0;
    return true;
#endif
}

/**
 *  \brief  判断线程是否正在运行
 *  \param  void
 *  \return 正在运行返回true,否则返回false
 *  \note   none
 */
bool Thread::isRunning()
{
	if (m_pRunnable==NULL)
	{
		return false;
	}
	return m_pRunnable->m_runFlag;
}

bool Thread::pthreadAttrSet(pthread_attr_t& threadAttr)
{
	struct sched_param param;
	if(pthread_attr_setstacksize(&threadAttr, m_threadAttribute.m_stackSize)!=0)
    {
        TRACE_ERR_CLASS("Set thread attribute stack size:%d failed!\n",m_threadAttribute.m_stackSize);
		return false;
    }
	
	/* 设置调度策略 */
    if (pthread_attr_setschedpolicy(&threadAttr, m_threadAttribute.m_policy)!=0)
	{
		TRACE_ERR_CLASS("Set thread attribute policy:%d failed\n",m_threadAttribute.m_policy);
		return false;
    }
	
	/* 设置调度优先级 */
    param.sched_priority = m_threadAttribute.m_priority;
    if (pthread_attr_setschedparam(&threadAttr, &param)!=0)
    {
		TRACE_ERR_CLASS("Set thread attribute priority:%d failed\n",m_threadAttribute.m_priority);
		return false;
    }

	/* 设置线程调度策略继承属性 */
	if (pthread_attr_setinheritsched(&threadAttr, m_threadAttribute.m_inherit)!=0)
	{
		TRACE_ERR_CLASS("Set thread sched inherit:%d failed\n",m_threadAttribute.m_inherit);
		return false;
    }
	return true;
}


/* 线程运行函数 */
void Thread::threadMain()
{
	m_threadStatus = STATE_RUNNING;
	m_threadID = pthread_self();
	if(m_pRunnable != NULL)
	{
		m_pRunnable->m_runFlag=true;
		m_pRunnable->run();
		m_pRunnable->m_runFlag=false;
	}
	m_threadStatus = STATE_EXIT;
	m_threadID = 0;
	pthread_exit(NULL);
}

/* 线程入口点 */
void * Thread::startRoutine(void *arg)
{
    pthread_detach(pthread_self());
    /* 设置detach属性,在线程结束后自动回收资源 */
	Thread* pThread = (Thread*)arg;
	if(pThread != NULL)
	{
		pThread->threadMain();
	}
	return (void*)0;
}

/**
 *  \brief  线程池初始化
 *  \param  maxThreadCount 最大线程个数
 *  \return 成功返回true,失败返回false
 */
bool ThreadPool::init(int maxThreadCount)
{
	if (maxThreadCount<=0)
    {
    	TRACE_ERR_CLASS("Thread number must be greater then 0!\n");
    	return false;
    }
	
    if (m_maxThreadCount>0)/* 已经初始化过了 */
    {
    	TRACE_ERR_CLASS("Thread Pool is allready inited!\n");
        return false;
    }

    m_maxThreadCount = maxThreadCount;
    for(auto i=0; i<m_maxThreadCount; i++)
    {
    	auto pThread = std::make_unique<Thread>();
        m_threadVect.push_back(std::move(pThread));
    }
    return true;
}
/**
 *  \brief  启动线程
 *  \param  runnable 线程运行体
 *  \param  priority 线程运行优先级
 *  \return 成功返回线程ID,失败返回STATUS_ERROR
 */
int ThreadPool::start(const Runnable& runnable, int policy, int priority)
{
    AutoLock lock(m_vectMutex);
    if (m_usedThreadCount>=m_maxThreadCount)
    {
        return RC_ERROR;
    }
    for(auto i=0; i<m_maxThreadCount; i++)
    {
        if (!m_threadVect[i]->isRunning())
        {
			m_threadVect[i]->setAttribute(policy,priority);
			if(!m_threadVect[i]->start(runnable))
			{
				return RC_ERROR;
			}
			m_usedThreadCount++;
	        return i; 
        }
    }
    return RC_ERROR; 
}
/**
 *  \brief  停止线程
 *  \param  threadID 线程id
 *  \return 成功返回true,失败返回false
 */
bool ThreadPool::cancel(int threadID)
{
    if (threadID<0 || threadID>=m_maxThreadCount)
    {
        return false;
    }
    AutoLock lock(m_vectMutex);
	if (m_threadVect[threadID]->isRunning())
	{
		if(!m_threadVect[threadID]->stop(2000000))
		{
			if(!m_threadVect[threadID]->forceQuit())
			{
				return false;
			}
		}
	}
	m_usedThreadCount--;
    return true;
}
/**
 *  \brief  返回当前线程池最大线程个数
 *  \param  none
 *  \return 线程个数
 */
int ThreadPool::maxThreadCount()
{
    return m_maxThreadCount;
}
/**
 *  \brief  返回当前可用线程个数
 *  \param  none
 *  \return 线程个数
 */
int ThreadPool::idleThreadCount()
{
    return m_maxThreadCount-m_usedThreadCount;
}

}

